﻿using System;
using System.Diagnostics;
using System.IO;
using System.IO.Packaging;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Windows.Shell;
using Alternet.Common;
using Alternet.Common.Projects.DotNet;
using Alternet.Editor;
using Alternet.Editor.Roslyn;
using Alternet.Scripter;
using Alternet.Scripter.Debugger;
using Alternet.Scripter.Debugger.UI;
using Alternet.Scripter.Integration;
using Alternet.Syntax.Parsers.Roslyn;
using Infragistics.Win.UltraMessageBox;
using Microsoft.CodeAnalysis.Editing;
using OrBitDataModule.Base;
using OrBitDataModule.Contract;
using OrBitDataModule.Models;

namespace DebuggerIntegration
{
    public partial class ScriptDebugger : Form
    {

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool SetForegroundWindow(IntPtr hWnd);

        private Alternet.Scripter.Debugger.ScriptDebugger debugger;

        private DebugCodeEditContainer codeEditContainer;
        private readonly string tempPath;

        private string DataProtocolScriptId = string.Empty;
        private string ScriptName = string.Empty;
        private string ScriptCode = string.Empty;
        private string ScriptDescription = string.Empty;

        public ScriptDebugger()
        {
            InitializeComponent();

            codeEditContainer = new DebugCodeEditContainer(editorsTabControl);
            codeEditContainer.EditorRequested += EditorContainer_EditorRequested;

            debugger = new Alternet.Scripter.Debugger.ScriptDebugger
            {
                ScriptRun = scriptRun,
            };

            debugger.DebuggerErrorOccured += Debugger_DebuggerErrorOccured;

            debuggerControlToolbar1.Debugger = debugger;
            debuggerControlToolbar1.DebuggerPreStartup += OnDebuggerPreStartup;

            debugMenu1.Debugger = debugger;
            debugMenu1.DebuggerPreStartup += OnDebuggerPreStartup;

            debuggerPanelsTabControl.Debugger = debugger;

            var controller = new DebuggerUIController(this, codeEditContainer)
            {
                Debugger = debugger,
                DebuggerPanels = debuggerPanelsTabControl
            };
            debuggerPanelsTabControl.Locals.Parent.Text = "局部变量";
            debuggerPanelsTabControl.Output.Parent.Text = "输出";
            debuggerPanelsTabControl.Breakpoints.Parent.Text = "断点";
            debuggerPanelsTabControl.CallStack.Parent.Text = "调用堆栈";
            debuggerPanelsTabControl.Watches.Parent.Text = "监视";
            debuggerPanelsTabControl.Threads.Parent.Text = "线程";
            debuggerPanelsTabControl.Errors.Parent.Text = "错误列表";
            debuggerPanelsTabControl.FindResults.Parent.Text = "查找结果";

            debuggerPanelsTabControl.Output.sslDebug.Text = "调试";
            debuggerPanelsTabControl.Output.sslGeneral.Text = "常规";
            codeEditContainer.Debugger = debugger;

            //debuggerPanelsTabControl.Output.lbOutput.TextChanged += LbOutput_TextChanged;
            codeEditContainer.Debugger.ExecutionResumed += Debugger_ExecutionResumed;

            ScaleControls();
            tempPath = AppDomain.CurrentDomain.BaseDirectory; //Path.Combine(Environment.GetEnvironmentVariable("TEMP"), "ScriptDebugger");

            label1.Text = string.Empty;
        }

        private void LbOutput_TextChanged(object sender, EventArgs e)
        {
            if (!debugger.IsStarted)
            {
                debuggerPanelsTabControl.Output.lbOutput.Clear();
            }
            //if (debuggerPanelsTabControl.Output.lbOutput.GetLineFromCharIndex(debuggerPanelsTabControl.Output.lbOutput.Text.Length) > 1000)
            //{
            //    debuggerPanelsTabControl.Output.lbOutput.Clear();
            //}
        }

        //private void Debugger_StateChanged(object sender, DebuggerStateChangedEventArgs e)
        //{
        //    Debug.WriteLine(Enum.GetName(typeof(DebuggerState), e.NewState));
        //    BeginInvoke(new Action(() =>
        //    {
        //        codeEditContainer.ActiveEditor.ReadOnly = e.NewState == DebuggerState.Running || e.NewState == DebuggerState.Startup;
        //    }));
        //}

        private void Debugger_ExecutionResumed(object sender, ExecutionResumedEventArgs e)
        {
            IntPtr mainWindowHandle = Process.GetCurrentProcess().MainWindowHandle;
            if (mainWindowHandle != IntPtr.Zero)
            {
                SetForegroundWindow(mainWindowHandle);
            }
        }
        internal void LoadScript(string uniqueId, string taskName, string scriptName, string scriptCode, string dataProtocolScriptId, string scriptDescription, string userName, string password)
        {
            string content = $"UniqueId = {uniqueId}\r\nUserName = {userName}\r\nPassword = {password}";
            File.WriteAllText(Path.Combine(tempPath, "params.conf"), content);
            File.WriteAllText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Project", "Script.cs"), scriptCode);
            OpenProject(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Project", "Scripter.csproj"));
            scriptRun.ScriptSource.WithDefaultReferences();
            //默认添加数据模块dll的引用
            AddReference(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "OrBitDataModule.dll"));
            AddReference("System.Data.dll");
            AddReference("System.Windows.Forms.dll");
            AddReference("Newtonsoft.Json.dll");
            AddReference("netstandard.dll");
            AddReference("System.Text.Json.dll");
            AddReference("System.Text.Encodings.Web.dll");
            AddReference("System.Text.Encoding.CodePages.dll");

            AddLocalReference();
            //string configPath = Path.Combine(tempPath, "App.config");
            //if (File.Exists(configPath))
            //{
            //    File.Delete(configPath);
            //    File.Copy(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "App.config"), configPath);
            //}

            if (Project.Files.Count > 0)
            {
                codeEditContainer.CloseFile(Project.Files[0]);
                codeEditContainer.TryActivateEditor(Project.Files[1]);
            }
            scriptRun.ScriptHost.ModulesDirectoryPath = tempPath;
            debugger.GeneratedModulesPath = tempPath;

            label1.Text = $"当前调试项目：{taskName},脚本名称：{scriptName}";

            this.DataProtocolScriptId = dataProtocolScriptId;
            this.ScriptName = scriptName;
            this.ScriptCode = scriptCode;
            this.ScriptDescription = scriptDescription;
        }



        private void AddLocalReference()
        {
            string directory = AppDomain.CurrentDomain.BaseDirectory;
            foreach (string file in Directory.GetFiles(directory, "*.dll"))
            {
                try
                {
                    if (Path.GetFileName(file).StartsWith("System.") && Path.GetFileName(file) != "System.EnterpriseServices.Wrapper.dll")
                    {
                        AddReference(Path.GetFileName(file));
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"加载 {Path.GetFileName(file)} 时出错：{ex.Message}");
                }
            }
        }

        private void OpenCSFile()
        {
            codeEditContainer.TryActivateEditor(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "DebuggerTest.cs"));
            scriptRun.ScriptSource.WithDefaultReferences();
            //scriptRun.ScriptSource.References.Add("System.Data.dll");//System.Data.dll
            //csParser.Repository.RegisterAssembly(dllFilePath);
            //scriptRun.ScriptSource.References.Add(@"..../***.dll");      
        }

        public void AddReference(string dllFilePath)
        {
            scriptRun.ScriptSource.References.Add(dllFilePath);
            Project.AddReference(Path.GetFileName(dllFilePath), dllFilePath);
            CodeEditExtensions.RegisterAssemblies(Project.ProjectExtension, Project.TryResolveAbsolutePaths(new string[] { dllFilePath }).ToArray(), keepExisting: true, projectName: Project.ProjectName);

        }
        protected DotNetProject Project { get; private set; } = new DotNetProject();

        public void SaveAllModifiedFiles()
        {
            foreach (var edit in codeEditContainer.Editors)
            {
                if (edit.Modified)
                    edit.SaveFile(edit.FileName);
            }
        }



        private void Debugger_DebuggerErrorOccured(object sender, DebuggerErrorOccuredEventArgs e)
        {
            BeginInvoke(new Action(() => MessageBox.Show(this, e.Exception.ToString(), "Debugger Error", MessageBoxButtons.OK, MessageBoxIcon.Error)));
        }

        private void OnDebuggerPreStartup(object sender, EventArgs e)
        {
            SaveAllModifiedFiles();
            SetScriptSource();
        }

        private bool SetScriptSource()
        {
            if (Project.HasProject)
                return true;

            if (codeEditContainer.ActiveEditor != null)
            {
                string fileName = codeEditContainer.ActiveEditor.FileName;
                if (new FileInfo(fileName).Exists)
                {
                    scriptRun.ScriptSource.FromScriptFile(fileName);
                    return true;
                }
            }

            return false;
        }

        private void ScaleControls()
        {
            if (!DisplayScaling.NeedsScaling)
                return;

            splitContainer.SplitterDistance = DisplayScaling.AutoScale(splitContainer.SplitterDistance);
        }

        private void OpenProject(string projectFilePath)
        {
            if (Project != null && Project.HasProject)
                CloseProject(Project);

            Project.Load(projectFilePath);
            scriptRun.ScriptSource.FromScriptProject(Project.ProjectFileName);
            var extension = Project.ProjectExtension;
            CodeEditExtensions.OpenProject(extension, Project);

            if (Project.Files.Count > 0)
            {
                codeEditContainer.TryActivateEditor(Project.Files[0]);
                (codeEditContainer.ActiveEditor as SyntaxEdit).Minimap.Visible = true;
            }

            debuggerPanelsTabControl.Errors.Clear();
        }

        private void CloseProject(DotNetProject project)
        {
            foreach (string fileName in project.Files)
            {
                CloseFile(fileName);
            }

            foreach (string fileName in project.Resources)
            {
                CloseFile(fileName);
            }

            var extension = string.Format(".{0}", project.DefaultExtension);

            CodeEditExtensions.CloseProject(extension, project.ProjectName);
            Project?.Reset();
            scriptRun.ScriptSource?.Reset();
        }

        private void CloseFile(string fileName)
        {
            codeEditContainer.CloseFile(fileName);
        }

        private string GetProjectName(string fileName)
        {
            if (Project.HasProject)
            {
                if (Project.Files.Contains(fileName, StringComparer.OrdinalIgnoreCase))
                    return Project.ProjectName;
            }

            return null;
        }

        private void EditorContainer_EditorRequested(object sender, DebugEditRequestedEventArgs e)
        {
            var edit = new DebugCodeEdit();
            var projectName = GetProjectName(e.FileName);
            edit.SetFileNameAndProject(e.FileName, projectName);
            edit.LoadFile(e.FileName);
            e.DebugEdit = edit;
        }

        private void ExitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Close();
        }

        private void CloseProjectToolStripMenuItem_Click(object sender, EventArgs e)
        {
            CloseProject(Project);
        }

        private void btn_save_Click(object sender, EventArgs e)
        {
            if (!string.IsNullOrWhiteSpace(this.DataProtocolScriptId)) UploadSource();
        }

        private async void UploadSource()
        {
            var editResult = await ScriptApi.AddScript(DataProtocolScriptId, ScriptName, ScriptCode, ScriptDescription);
            UltraMessageBoxManager.Show(this, editResult.Message, editResult.Result ? "提示" : "错误", MessageBoxButtons.OK, editResult.Result ? MessageBoxIcon.Information : MessageBoxIcon.Error);
        }
    }
}